/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/
#include "MkPipe.h"
#include "MkSocket/MkSocket.h"
#include "MkEvent.h"
#include "MkUtil/MkLog.h"

CMkPipe::CMkPipe(CMkEvent* pEvent)
    : m_pEvent(pEvent)
{
#ifndef _WIN32
    m_PipFd[0] = INVALID_SOCKET;
    m_PipFd[1] = INVALID_SOCKET;
#endif
}

Uint32 CMkPipe::Start(const MkFunction<void()>& Cb)
{
    m_Cb = Cb;
    Uint32 ErrorCode = NoneError;
#ifdef _WIN32
    struct sockaddr_in listen_addr;
    struct sockaddr_in connect_addr;
    SOCKET SockListener = INVALID_SOCKET;
    SOCKET SockConnector = INVALID_SOCKET;
    SOCKET SockAcceptor = INVALID_SOCKET;
    socklen_t nLen = 0;

    SockListener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    SockConnector = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (INVALID_SOCKET == SockListener || INVALID_SOCKET == SockConnector) {
        ErrorCode = SocketCreateFailed;
        goto fail;
    }

    memset(&listen_addr, 0, sizeof(listen_addr));
    listen_addr.sin_family = AF_INET;
    listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    listen_addr.sin_port = 0;	/* kernel chooses port.	 */
    if (bind(SockListener, (struct sockaddr *) &listen_addr, sizeof(listen_addr))
        == -1) {
        ErrorCode = SocketBindFailed;
        goto fail;
    }
    if (listen(SockListener, 1) == -1) {
        ErrorCode = SocketListenFailed;
        goto fail;
    }

    nLen = sizeof(connect_addr);
    if (getsockname(SockListener, (struct sockaddr *) &connect_addr, &nLen) == -1
        || nLen != sizeof(connect_addr)) {
        ErrorCode = SocketSetOptFailed;
        goto fail;
    }

    if (connect(SockConnector, (struct sockaddr *) &connect_addr,
        sizeof(connect_addr)) == -1) {
        ErrorCode = SocketConnectFailed;
        goto fail;
    }

    nLen = sizeof(listen_addr);
    SockAcceptor = accept(SockListener, (struct sockaddr *) &listen_addr, &nLen);
    if (INVALID_SOCKET == SockAcceptor || nLen != sizeof(listen_addr)) {
        ErrorCode = SocketAcceptFailed;
        goto fail;
    }

    if (getsockname(SockConnector, (struct sockaddr *) &connect_addr, &nLen) == -1) {
        ErrorCode = SocketSetOptFailed;
        goto fail;

    }

    if (nLen != sizeof(connect_addr)
        || listen_addr.sin_family != connect_addr.sin_family
        || listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr
        || listen_addr.sin_port != connect_addr.sin_port) {
        ErrorCode = UnknownError;
        goto fail;
    }
    MkCloseSocket(SockListener);
    m_PipFd[1] = SockAcceptor;
    m_PipFd[0] = SockConnector;

    unsigned long ul = 1;
    int nRet = ioctlsocket(SockAcceptor, FIONBIO, &ul);
    if (-1 == nRet) {
        return SocketIoctlFailed;
    }
    nRet = ioctlsocket(SockConnector, FIONBIO, &ul);
    if (-1 == nRet) {
        return SocketIoctlFailed;
    }
    m_pEvent->AddEventer(m_PipFd[0], CMkEvent::EventRead, [this](Uint32 Event) {
        OnRecv();
    });
    return NoneError;
fail:
    MkCloseSocket(SockListener);
    MkCloseSocket(SockAcceptor);
    MkCloseSocket(SockConnector);
    return ErrorCode;
#else
    int nRet = pipe(m_PipFd);
    //MkDebugLog("pip result:%d %d-%d\n", nRet, m_PipFd[0],m_PipFd[1]);
    if (0 != nRet) {
        return SystemError;
    }
    m_pEvent->AddEventer(m_PipFd[0], CMkEvent::EventRead, [this](Uint32 Event) {
        OnRecv();
    });
    return NoneError;
#endif
}

Uint32 CMkPipe::Active()
{
#ifdef _WIN32
    int nRet = send(m_PipFd[1], "2", 1, 0);
#else
    int nRet = write(m_PipFd[1], "2", 1);
#endif
    if (nRet <= 0) {
        return SocketSendFailed;
    }
    return NoneError;
}

Uint32 CMkPipe::OnRecv()
{
    Int8 pbuf[Len32] = { 0 };
    Uint32 nLen = Len32;
    Uint32 ErrorCode = NoneError;
#ifdef _WIN32
    int nRet = recv(m_PipFd[0], pbuf, nLen, 0);
#else
    int nRet = read(m_PipFd[0], pbuf, nLen);
    ErrorCode = nRet > 0 ? NoneError : SystemError;
#endif
    MkCheckErrorLog(ErrorCode, "pip recv buffer failed\n");
    if (m_Cb) {
        m_Cb();
    }
    return NoneError;
}

CMkPipe::~CMkPipe()
{
    closesocket(m_PipFd[1]);
    closesocket(m_PipFd[0]);
}